/*
 * Copyright 2010 Michael Nitsche
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.mnit.basis.sys;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;

import de.mnit.basis.daten.FormatText;
import de.mnit.basis.daten.WandleObjekt;
import de.mnit.basis.daten.konstant.position.POSITION_H;
import de.mnit.basis.daten.typ.zeit.Uhrzeit;
import de.mnit.basis.daten.typ.zeit.Zeitpunkt;
import de.mnit.basis.daten.typ.zeit.format.FORMAT_ZEIT;
import de.mnit.basis.daten.typ.zeit.format.FormatZeit;
import de.mnit.basis.fehler.Fehler;
import de.mnit.basis.fehler.S_Fehler;
import de.mnit.basis.sys.thread.BlockSchranke;

/**
 * @author Michael Nitsche
 *
 * Ausgabe von mehreren zusammengehörigen Zeilen mit \n getrennt übergeben
 */
public class Ausgabe {

	private static boolean fehler_kompakt = true;
	private static boolean produktiv=false;
	private static boolean log=false;
	private static final BlockSchranke schranke = new BlockSchranke();


	// SETZE

	public static void sProduktiv() {
		produktiv=true;
		fehler_kompakt=true;
	}

	public static void sDetailliert() {
		produktiv=false;
		fehler_kompakt=false;
	}

	public static void aktiviereLog() {
		if(!Log.gAktiv())
			Fehler.sonstige.da_Verboten("Log nicht aktiviert!");
		else
			log=true;
	}

	// AUSGABE

	public static void fehler(String text)   { fehler(null, text); }
	public static void fehler(Throwable t)   { fehler(t, null); }

	public static void fehler(Throwable t, String text) {
		boolean grund=text!=null && text.length()>0;
		if(text==null) text="";

		if(t==null)
			t=new RuntimeException();
		StackTraceElement[] sta=t.getStackTrace();
		text+=(grund ? "\n^ " : "")+t.getClass().getName()+": "+t.getMessage();

		if(t instanceof S_Fehler) {
			S_Fehler bf=(S_Fehler)t;
			for(Object o : bf.gAnhang())
				text+="\n"+WandleObjekt.zuTextDebug(o);
		}
		int anzahl=0;
		int noch=sta.length;
		for(StackTraceElement st : sta) {
			noch--;
			String stt=st.toString();
			boolean letztes= noch==0;
			if(stt.matches("^(sun|java|org)\\..*$")) continue;
			if(stt.matches("^.*:1\\)$")) continue;
			if(!produktiv && fehler_kompakt) {
				if(stt.matches("^.*\\.(BasisFehler|Fehler)\\..*$")) continue;
				if(stt.matches("^mn.basis.fehler.*$")) continue;
//				if(anzahl>=5 && !letztes) continue;  //Nicht so gut, da bei SWT sehr oft wichtige Zeilen fehlen
			}
			anzahl++;
			text+="\n"+"        "+(letztes ? ">" : "@")+" "+stt;
		}
//		Ausgabe.fehler(text);
//		t.printStackTrace();
		if(t.getCause()!=null) fehler(t.getCause(),text);
		else iSchreib("FEHLER", text, true, false);
	}

	public static void text(Object... oa) {
		StringBuilder sb=new StringBuilder();
		for(int i=0; i<oa.length; i++) {
			if(i>0) sb.append('\n');
			sb.append(WandleObjekt.zuTextAusgabe(oa[i]));
		}
		if(produktiv) iSchreib("TEXT"  ,sb.toString(),false,false);
		else {
			StackTraceElement[] trace = new Throwable().getStackTrace();
			String link=trace[1].toString();
			iSchreib("TEXT",sb.toString()+"\n        @ "+link,false,true);
		}
	}

	public static void debugDetail(Object... oa) {
		StackTraceElement[] sta = new Throwable().getStackTrace();
		String text="===== Debug-Detail-Punkt =====\n";
		for(int i=0; i<oa.length; i++) {
			if(i>0) text+='\n';
			text+=WandleObjekt.zuTextDebug(oa[i]);
		}
		int noch=sta.length;
		for(StackTraceElement st : sta) {
			noch--;
			String stt=st.toString();
			if(stt.matches("^(sun|java|org)\\..*$")) continue;
			if(stt.matches("^.*:1\\)$")) continue;
			boolean letztes= noch==0;
//			boolean letztes=st.equals(sta[sta.length-1]);
			text+="\n"+"        "+(letztes ? ">" : "@")+" "+stt;
		}
		iSchreib("DEBUG", text, false, true);
	}

	public static void debug() { //TODO gehört mit den anderen Debug-Methoden verbunden!
		StackTraceElement[] trace = new Throwable().getStackTrace();
		String link=trace[1].toString();
		iSchreib("DEBUG","DEBUG   @ "+link,false,true);
	}

	public static void debug(Object o) {
		debugVersetzt(1, o);
	}

	public static void debug(Object o1, Object... or) {
		debugVersetzt(1,o1,or);
	}

	public static void debugVersetzt(int offset, Object o1, Object... or) {
		StackTraceElement[] trace = new Throwable().getStackTrace();
		Fehler.zahl.wenn_Ausserhalb(0, Math.min(trace.length-2,3), offset);
		String link=trace[offset+1].toString();
		String text=WandleObjekt.zuTextDebug(o1)+"\n"; //zuAusgabe
		for(Object o : or) text+=WandleObjekt.zuTextDebug(o)+"\n"; //zuAusgabe
		text=iOptimieren(text);
		iSchreib("DEBUG",text+"        @ "+link,false,true);
	}

	public static void warnung(Object... or) {
		//TODO die nachfolgenden 3 Zeilen evtl. in iSchreib integrieren!
		String text="";
		for(Object o : or)
			text+=WandleObjekt.zuTextAusgabe(o)+"\n"; //zuAusgabe
		text=iOptimieren(text);
		StackTraceElement[] trace = new Throwable().getStackTrace();
		String link=trace[1].toString();
		iSchreib("WARNUNG",text+"        @ "+link, true, false);
	}

	public static OutputStream streamText()     { return iStream("TEXT"  ,false,false); }
	public static OutputStream streamFehler()   { return iStream("FEHLER",true ,false); }

	// INTERN

	private static PrintStream iStream(final String titel, final boolean fehler, final boolean debug) {
		return new PrintStream(new OutputStream(){
			private StringBuilder sb = new StringBuilder();

			public void write(int i) throws IOException {
				sb.append( new String(new byte[]{(byte)i}) );
			}

			public void close() throws IOException {
				super.close();
				iSchreib(titel,sb.toString(),fehler,debug);
			}
		});
	}

	private synchronized static void iSchreib(String titel, String text/*o*/, boolean fehler, final boolean debug) {
		if(debug && produktiv) return; //TODO Hm???

		Thread.currentThread().getName();
		boolean doppelt=schranke.eingang();
		//Achtung, alles was Fehler verursachen könnte muß bei doppelt raus!
		try {
			if(doppelt) {
				if(fehler) text=System.currentTimeMillis()+'\n'+text;
				text=titel+"\n"+text;
				if(fehler) System.err.print(text);
				else System.out.print(text);
			}
			else {
				if(fehler) text=Zeitpunkt.neu().zuTextEDV()+'\n'+text;

				String zeit="";
				if(!produktiv)
//					zeit=Uhrzeit.neu().zuText(UHRZEIT_FORMAT.F_MIN_2, UHRZEIT_FORMAT.F_SEK_2);
					zeit=FormatZeit.zuText(Uhrzeit.neu(), FORMAT_ZEIT.MIN_2, FORMAT_ZEIT.SEK_2);

				//		String text=Wandle.objekt.zuDebug(o);
				titel=produktiv ? "" : FormatText.breite(8,' ',titel,POSITION_H.LINKS)+zeit+"|";
				text=titel+text.replaceAll("\n","\n"+titel)+"\n";

				if(fehler) System.err.print(text);
				else System.out.print(text);
				if(log) Log.schreib(text);
			}
		}
		finally {
			schranke.ausgang();
		}
	}

	private static String iOptimieren(String text) {
		if(text.length()>=2 && text.charAt(text.length()-2)=='\n')
			return text.substring(0, text.length()-1);
		return text;
	}

}
